<?php

/**
 * CCoreMimeHeader
 * CCoreMimeHeadersCollection
 *
 * @author Usenko Timur <usenko.timur@gmail.com>
 * @package mime
 */

CCore::Using('base.collection');
CCore::Using('base.utils');
CCore::Using('mime.constants');
CCore::Using('mime.parameter');
CCore::Using('mime.utils');
CCore::Using('mime.part');

/**
 * @package mime
 */
class CCoreMimeHeader
{
	#<editor-fold defaultstate="collapsed" desc="private">
	/**
	 * @var string
	 */
	private $_sName;

	/**
	 * @var string
	 */
	private $_sValue;

	/**
	 * @var string
	 */
	private $_sDecodedValue;

	/**
	 * @var string
	 */
	private $_sRawValue;

	/**
	 * @var CCoreMimeParametersCollection
	 */
	private $_oParameters;

	/**
	 * @var CCoreMimeHeadersCollection
	 */
	private $_oParent;
	#</editor-fold>

	/**
	 * @param string $sName
	 * @param string $sValue = null
	 * @return CCoreMimeHeader
	 */
	public function __construct($sName, $sValue = null, $bIsParameterHeader = false)
	{
		CCore::CheckTypes(array('$sName', '$sValue'));
		
		$this->_sName = trim($sName);
		$this->_oParameters = null;
		$this->_sDecodedValue = null;
		$this->_sRawValue = (null === $sValue) ? null : trim($sValue);
		if ($bIsParameterHeader && null !== $this->_sRawValue)
		{
			$aRawExplode = explode(';', $this->_sRawValue, 2);
			if (2 === count($aRawExplode))
			{
				$this->_sValue = $aRawExplode[0];
				$this->_oParameters = new CCoreMimeParametersCollection($aRawExplode[1]);
			}
			else
			{
				$this->_sValue = $this->_sRawValue;
			}
		}
		else
		{
			$this->_sValue = $this->_sRawValue;
		}

		$this->_oParent = null;
	}

	/**
	 * @return string
	 */
	public function GetCharset()
	{
		return (null === $this->_oParent) ? CCoreMimePart::$DefaultCharset : $this->_oParent->GetCharset();
	}

	/**
	 * @param CCoreMimeHeadersCollection &$oMimeHeadersCollection
	 * @return void
	 */
	public function SetParent(CCoreMimeHeadersCollection &$oMimeHeadersCollection)
	{
		$this->_oParent =& $oMimeHeadersCollection;
	}

	/**
	 * @param string $sOutputCharset = C_MIME_CHARSET_UTF8
	 * @return string
	 */
	public function DecodedValue($sOutputCharset = C_MIME_CHARSET_UTF8)
	{
		CCore::CheckTypes(array('$sOutputCharset'));
		
		if (null === $this->_sDecodedValue)
		{
			$this->_sDecodedValue = CCoreMimeUtils::DecodeHeaderValue(
				$this->_sValue, $this->GetCharset(), C_MIME_CHARSET_UTF8);
		}

		return CCoreBaseUtils::ConvertEncoding($this->_sDecodedValue, C_MIME_CHARSET_UTF8, $sOutputCharset);
	}

	/**
	 * @todo
	 * @param string $sOutputCharset = C_MIME_CHARSET_UTF8
	 * @return string
	 */
	public function EncodedValue($sOutputCharset = C_MIME_CHARSET_UTF8)
	{
		CCore::CheckTypes(array('$sOutputCharset'));

		#TODO: encode value

		return $this->_sValue;
	}

	/**
	 * @return string
	 */
	public function Name()
	{
		return $this->_sName;
	}

	/**
	 * @return string
	 */
	public function RawValue()
	{
		return $this->_sRawValue;
	}
	
	/**
	 * @return CCoreMimeParametersCollection
	 */
	public function Parameters()
	{
		return $this->_oParameters;
	}
}

/**
 * @package mime
 */
class CCoreMimeHeadersCollection extends ACoreBaseCollection
{
	/**
	 * @var bool
	 */
	static $StoreRawHeaders = true;
	
	#<editor-fold defaultstate="collapsed" desc="private">
	/**
	 * @return string
	 */
	private $_sRawHeaders;

	/**
	 * @return CCoreMimePart
	 */
	private $_oParent;
	#</editor-fold>
	
	/**
	 * @param string $nsRawHeaders = null
	 * @return CCoreMimeHeadersCollection
	 */
	public function __construct($nsRawHeaders = null)
	{
		CCore::CheckTypes(array('$nsRawHeaders'));

		parent::__construct();

		$this->_oParent = null;
		$this->_sRawHeaders = '';

		if (null !== $nsRawHeaders)
		{
			$this->Parse($nsRawHeaders);
		}
	}

	/**
	 * @param string $sRawHeaders
	 * @return void
	 */
	public function Parse($sRawHeaders)
	{
		if (self::$StoreRawHeaders)
		{
			$this->_sRawHeaders = $sRawHeaders;
		}

		$this->Clear();

		$aHeaders = explode("\n", str_replace("\r", '', $sRawHeaders));

		$sName = $sValue = null;
		foreach ($aHeaders as $sHeadersValue)
		{
			if (empty($sHeadersValue))
			{
				continue;
			}

			$sFirstChar = substr($sHeadersValue, 0, 1);
			if ($sFirstChar === ' ' || $sFirstChar === "\t")
			{
				$sValue .= C_MIME_CRLF.$sHeadersValue;
			}
			else
			{
				if (null !== $sName)
				{
					$oHeader = new CCoreMimeHeader($sName, $sValue, $this->_isParamHeaderName($sName));
					$this->Add($oHeader);
					$sName = $sValue = null;
					unset($oHeader);
				}

				$aHeaderParts = explode(':', $sHeadersValue, 2);
				$sName = $aHeaderParts[0];
				$sValue = isset($aHeaderParts[1]) ? $aHeaderParts[1] : '';
			}
		}

		if (null !== $sName)
		{
			$oHeader = new CCoreMimeHeader($sName, $sValue, $this->_isParamHeaderName($sName));
			$this->Add($oHeader);
			unset($oHeader);
		}
	}

	/**
	 * @return string
	 */
	public function GetCharset()
	{
		return (null === $this->_oParent) ? CCoreMimePart::$DefaultCharset : $this->_oParent->GetCharset();
	}
	
	/**
	 * @return string
	 */
	public function GetRawHeaders()
	{
		return $this->_sRawHeaders;
	}

	/**
	 * @param CCoreMimePart &$oMimePart
	 * @param bool $bFillParentByCharsetValue = false
	 * @return void
	 */
	public function SetParent(CCoreMimePart &$oMimePart, $bFillParentByCharsetValue = false)
	{
		$this->_oParent =& $oMimePart;
		if ($bFillParentByCharsetValue)
		{
			$oMimePart->SetCharset($this->GetCharsetHeaderValue());
		}
	}
	
	/**
	 * @param CCoreMimeHeader $oMimeHeader
	 * @return void
	 */
	public function Add(CCoreMimeHeader $oMimeHeader)
	{
		CCore::CheckTypes(array('$oMimeHeader'));

		$oMimeHeader->SetParent($this);
		parent::Add($oMimeHeader);
	}

	/**
	 * @return string
	 */
	public function GetBoundaryHeaderValue()
	{
		return trim($this->GetParamValueFromHeader(C_MIME_HEADERS_CONTENT_TYPE, 'boundary'));
	}

	/**
	 * @return string
	 */
	public function GetCharsetHeaderValue()
	{
		return trim(strtolower($this->GetParamValueFromHeader(C_MIME_HEADERS_CONTENT_TYPE, 'charset')));
	}

	/**
	 * @return string
	 */
	public function GetContentTransferEncodingValue()
	{
		return trim(strtolower($this->GetValueByName(C_MIME_HEADERS_CONTENT_TRANSFER_ENCODING)));
	}
	
	/**
	 * @return string
	 */
	public function GetContentTypeValue()
	{
		return trim(strtolower($this->GetValueByName(C_MIME_HEADERS_CONTENT_TYPE)));
	}

	/**
	 * @param string $sHeaderName
	 * @return string
	 */
	public function GetValueByName($sHeaderName)
	{
		CCore::CheckTypes(array('$sHeaderName'));

		$oHeader =& $this->_getByName($sHeaderName);
		return (null !== $oHeader) ? $oHeader->EncodedValue() : '';
	}

	/**
	 * @param string $sHeaderName
	 * @return array
	 */
	public function GetValuesByName($sHeaderName)
	{
		CCore::CheckTypes(array('$sHeaderName'));

		$aResult = array();
		$sHeaderNameLower = strtolower($sHeaderName);
		$aHeaders =& $this->GetAsArray();
		foreach ($aHeaders as /* @var $oHeader CCoreMimeHeader */ &$oHeader)
		{
			if ($sHeaderNameLower === strtolower($oHeader->Name()))
			{
				$aResult[] = $oHeader->EncodedValue();
			}
		}
		return $aResult;
	}

	/**
	 * @param string $sHeaderName
	 * @param string $sOutputCharset = C_MIME_CHARSET_UTF8
	 * @return string
	 */
	public function GetDecodedValueByName($sHeaderName, $sOutputCharset = C_MIME_CHARSET_UTF8)
	{
		CCore::CheckTypes(array('$sHeaderName'));

		$oHeader =& $this->_getByName($sHeaderName);
		return (null !== $oHeader) ? $oHeader->DecodedValue($sOutputCharset) : '';
	}

	/**
	 * @param string $sHeaderName
	 * @param string $sParamName
	 * @return string
	 */
	public function GetParamValueFromHeader($sHeaderName, $sParamName)
	{
		CCore::CheckTypes(array('$sHeaderName', '$sParamName'));

		$sResult = '';
		$oHeader =& $this->_getByName($sHeaderName);
		if ($oHeader)
		{
			$oParameters = $oHeader->Parameters();
			$sResult = ($oParameters) ? $oParameters->GetParamValueByName($sParamName) : '';
		}
		return $sResult;
	}

	/**
	 * @param string $sHeaderName
	 * @param string $sParamName
	 * @param string $sOutputCharset = C_MIME_CHARSET_UTF8
	 * @return string
	 */
	public function GetParamDecodedValueFromHeader($sHeaderName, $sParamName, $sOutputCharset = C_MIME_CHARSET_UTF8)
	{
		CCore::CheckTypes(array('$sHeaderName', '$sParamName', '$sOutputCharset'));

		$sResult = '';
		$oHeader =& $this->_getByName($sHeaderName);
		if ($oHeader)
		{
			$oParameters = $oHeader->Parameters();
			$sResult = ($oParameters)
				? CCoreMimeUtils::DecodeHeaderValue(
					$oParameters->GetParamValueByName($sParamName), $this->GetCharset(), $sOutputCharset)
				: '';
		}
		return $sResult;
	}

	/**
	 * @param string $sHeaderName
	 * @return CCoreMimeHeader
	 */
	private function &_getByName($sHeaderName)
	{
		CCore::CheckTypes(array('$sHeaderName'));

		$nReturnNull = null;
		$sHeaderNameLower = strtolower($sHeaderName);
		$aHeaders =& $this->GetAsArray();
		foreach ($aHeaders as /* @var $oHeader CCoreMimeHeader */ &$oHeader)
		{
			if ($sHeaderNameLower === strtolower($oHeader->Name()))
			{
				$nReturnNull =& $oHeader;
				break;
			}
		}
		return $nReturnNull;
	}

	/**
	 * @todo
	 * @param string $sName
	 * @return bool
	 */
	private function _isParamHeaderName($sName)
	{
		$sLowerName = strtolower($sName);
		return in_array($sLowerName,
			array(strtolower(C_MIME_HEADERS_CONTENT_TYPE), strtolower(C_MIME_HEADERS_CONTENT_DISPOSITION)));
	}
}
